home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 21 / AACD 21.iso / AACD / Utilities / Ghostscript / src / gdevbmpa.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-01  |  26.8 KB  |  717 lines

  1. /* Copyright (C) 1998, 1999, 2000 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of AFPL Ghostscript.
  4.   
  5.   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
  6.   distributor accepts any responsibility for the consequences of using it, or
  7.   for whether it serves any particular purpose or works at all, unless he or
  8.   she says so in writing.  Refer to the Aladdin Free Public License (the
  9.   "License") for full details.
  10.   
  11.   Every copy of AFPL Ghostscript must include a copy of the License, normally
  12.   in a plain ASCII text file named PUBLIC.  The License grants you the right
  13.   to copy, modify and redistribute AFPL Ghostscript, but only under certain
  14.   conditions described in the License.  Among other things, the License
  15.   requires that the copyright notice and this notice be preserved on all
  16.   copies.
  17. */
  18.  
  19. /*$Id: gdevbmpa.c,v 1.3 2000/09/19 19:00:11 lpd Exp $ */
  20. /* .BMP file format output drivers: Demo of ASYNC rendering */
  21.  
  22. /* 2000-04-20 ghost@aladdin.com - Makes device structures const, changing
  23.    makefile entry from DEV to DEV2. */
  24. /* 1998/12/29 ghost@aladdin.com - Modified to use gdev_prn_render_lines,
  25.    which replaces the former "overlay" calls */
  26. /* 1998/11/23 ghost@aladdin.com - Removed pointless restriction to
  27.    single-page output */
  28. /* 1998/7/28 ghost@aladdin.com - Factored out common BMP format code
  29.    to gdevbmpc.c */
  30. /* Initial version 2/2/98 by John Desrosiers (soho@crl.com) */
  31.  
  32. #include "stdio_.h"
  33. #include "gserrors.h"
  34. #include "gdevprna.h"
  35. #include "gdevpccm.h"
  36. #include "gdevbmp.h"
  37. #include "gdevppla.h"
  38. #include "gpsync.h"
  39.  
  40. /*
  41.  * The original version of this driver was restricted to producing a single
  42.  * page per file.  If for some reason you want to reinstate this
  43.  * restriction, uncomment the next line. 
  44.  * NOTE: Even though the logic for multi-page files is straightforward,
  45.  * it results in a file that most programs that process BMP format cannot
  46.  * handle. Most programs will only display the first page.
  47.  */
  48. /*************** #define SINGLE_PAGE ****************/
  49.  
  50. /* ------ The device descriptors ------ */
  51.  
  52. /* Define data type for this device based on prn_device */
  53. typedef struct gx_device_async_s {
  54.     gx_device_common;
  55.     gx_prn_device_common;
  56.     bool UsePlanarBuffer;
  57.     int buffered_page_exists;
  58.     long file_offset_to_data[4];
  59. } gx_device_async;
  60.  
  61. /* Define initializer for device */
  62. #define async_device(procs, dname, w10, h10, xdpi, ydpi, lm, bm, rm, tm, color_bits, print_page)\
  63. { prn_device_std_margins_body(gx_device_async, procs, dname,\
  64.     w10, h10, xdpi, ydpi, lm, tm, lm, bm, rm, tm, color_bits, print_page),\
  65.     0, 0, { 0, 0, 0, 0 }\
  66. }
  67.  
  68. private dev_proc_open_device(bmpa_writer_open);
  69. private dev_proc_open_device(bmpa_cmyk_writer_open);
  70. private prn_dev_proc_open_render_device(bmpa_reader_open_render_device);
  71. private dev_proc_print_page_copies(bmpa_reader_print_page_copies);
  72. /* VMS limits procedure names to 31 characters. */
  73. private dev_proc_print_page_copies(bmpa_cmyk_reader_print_copies);
  74. private prn_dev_proc_buffer_page(bmpa_reader_buffer_page);
  75. private prn_dev_proc_buffer_page(bmpa_cmyk_reader_buffer_page);
  76. private dev_proc_output_page(bmpa_reader_output_page);
  77. private dev_proc_get_params(bmpa_get_params);
  78. private dev_proc_put_params(bmpa_put_params);
  79. private dev_proc_get_hardware_params(bmpa_get_hardware_params);
  80. private prn_dev_proc_start_render_thread(bmpa_reader_start_render_thread);
  81. private prn_dev_proc_get_space_params(bmpa_get_space_params);
  82. #define default_print_page 0    /* not needed becoz print_page_copies def'd */
  83.  
  84. /* Monochrome. */
  85.  
  86. private const gx_device_procs bmpamono_procs =
  87.   prn_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close);
  88. const gx_device_async gs_bmpamono_device =
  89.   async_device(bmpamono_procs, "bmpamono",
  90.     DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
  91.     X_DPI, Y_DPI,
  92.     0,0,0,0,            /* margins */
  93.     1, default_print_page);
  94.  
  95. /* 1-bit-per-plane separated CMYK color. */
  96.  
  97. #define bmpa_cmyk_procs(p_open, p_map_color_rgb, p_map_cmyk_color)\
  98.     p_open, NULL, NULL, gdev_prn_output_page, gdev_prn_close,\
  99.     NULL, p_map_color_rgb, NULL, NULL, NULL, NULL, NULL, NULL,\
  100.     bmpa_get_params, bmpa_put_params,\
  101.     p_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device
  102.  
  103. private const gx_device_procs bmpasep1_procs = {
  104.     bmpa_cmyk_procs(bmpa_cmyk_writer_open, cmyk_1bit_map_color_rgb,
  105.             cmyk_1bit_map_cmyk_color)
  106. };
  107. const gx_device_async gs_bmpasep1_device = {
  108.   prn_device_body(gx_device_async, bmpasep1_procs, "bmpasep1",
  109.     DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
  110.     X_DPI, Y_DPI,
  111.     0,0,0,0,            /* margins */
  112.     4, 4, 1, 1, 2, 2, default_print_page)
  113. };
  114.  
  115. /* 8-bit-per-plane separated CMYK color. */
  116.  
  117. private const gx_device_procs bmpasep8_procs = {
  118.     bmpa_cmyk_procs(bmpa_cmyk_writer_open, cmyk_8bit_map_color_rgb,
  119.             cmyk_8bit_map_cmyk_color)
  120. };
  121. const gx_device_async gs_bmpasep8_device = {
  122.   prn_device_body(gx_device_async, bmpasep8_procs, "bmpasep8",
  123.     DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
  124.     X_DPI, Y_DPI,
  125.     0,0,0,0,            /* margins */
  126.     4, 32, 255, 255, 256, 256, default_print_page)
  127. };
  128.  
  129. /* 4-bit (EGA/VGA-style) color. */
  130.  
  131. private const gx_device_procs bmpa16_procs =
  132.   prn_color_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close,
  133.     pc_4bit_map_rgb_color, pc_4bit_map_color_rgb);
  134. const gx_device_async gs_bmpa16_device =
  135.   async_device(bmpa16_procs, "bmpa16",
  136.     DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
  137.     X_DPI, Y_DPI,
  138.     0,0,0,0,            /* margins */
  139.     4, default_print_page);
  140.  
  141. /* 8-bit (SuperVGA-style) color. */
  142. /* (Uses a fixed palette of 3,3,2 bits.) */
  143.  
  144. private const gx_device_procs bmpa256_procs =
  145.   prn_color_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close,
  146.     pc_8bit_map_rgb_color, pc_8bit_map_color_rgb);
  147. const gx_device_async gs_bmpa256_device =
  148.   async_device(bmpa256_procs, "bmpa256",
  149.     DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
  150.     X_DPI, Y_DPI,
  151.     0,0,0,0,            /* margins */
  152.     8, default_print_page);
  153.  
  154. /* 24-bit color. */
  155.  
  156. private const gx_device_procs bmpa16m_procs =
  157.   prn_color_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close,
  158.     bmp_map_16m_rgb_color, bmp_map_16m_color_rgb);
  159. const gx_device_async gs_bmpa16m_device =
  160.   async_device(bmpa16m_procs, "bmpa16m",
  161.     DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
  162.     X_DPI, Y_DPI,
  163.     0,0,0,0,            /* margins */
  164.     24, default_print_page);
  165.  
  166. /* 32-bit CMYK color (outside the BMP specification). */
  167.  
  168. private const gx_device_procs bmpa32b_procs = {
  169.     bmpa_cmyk_procs(bmpa_writer_open, gx_default_map_color_rgb,
  170.             gx_default_cmyk_map_cmyk_color)
  171. };
  172. const gx_device_async gs_bmpa32b_device =
  173.   async_device(bmpa32b_procs, "bmpa32b",
  174.            DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
  175.            X_DPI, Y_DPI,
  176.            0, 0, 0, 0,        /* margins */
  177.            32, default_print_page);
  178.  
  179. /* --------- Forward declarations ---------- */
  180.  
  181. private void bmpa_reader_thread(P1(void *));
  182.  
  183. /* ------------ Writer Instance procedures ---------- */
  184.  
  185. /* Writer's open procedure */
  186. private int
  187. bmpa_open_writer(gx_device *pdev  /* Driver instance to open */,
  188.          dev_proc_print_page_copies((*reader_print_page_copies)),
  189.          prn_dev_proc_buffer_page((*reader_buffer_page)))
  190. {
  191.     gx_device_async * const pwdev = (gx_device_async *)pdev;
  192.     int max_width;
  193.     int max_raster;
  194.     int min_band_height;
  195.     int max_src_image_row;
  196.  
  197.     /*
  198.      * Set up device's printer proc vector to point to this driver, since
  199.      * there are no convenient macros for setting them up in static template.
  200.      */
  201.     init_async_render_procs(pwdev, bmpa_reader_start_render_thread,
  202.                 reader_buffer_page,
  203.                 reader_print_page_copies);
  204.     set_dev_proc(pdev, get_params, bmpa_get_params);    /* because not all device-init macros allow this to be defined */
  205.     set_dev_proc(pdev, put_params, bmpa_put_params);    /* ibid. */
  206.     set_dev_proc(pdev, get_hardware_params, bmpa_get_hardware_params);
  207.     set_dev_proc(pdev, output_page, bmpa_reader_output_page);    /* hack */
  208.     pwdev->printer_procs.get_space_params = bmpa_get_space_params;
  209.     pwdev->printer_procs.open_render_device =
  210.     bmpa_reader_open_render_device;    /* Included for tutorial value */
  211.  
  212.     /*
  213.      * Determine MAXIMUM parameters this device will have to support over
  214.      * lifetime.  See comments for bmpa_get_space_params().
  215.      */
  216.     max_width = DEFAULT_WIDTH_10THS * 60;   /* figure max wid = default @ 600dpi */
  217.     min_band_height = max(1, (DEFAULT_HEIGHT_10THS * 60) / 100);
  218.     max_raster = bitmap_raster(max_width * pwdev->color_info.depth);    /* doesn't need to be super accurate */
  219.     max_src_image_row = max_width * 4 * 2;
  220.  
  221.     /* Set to planar buffering mode if appropriate. */
  222.     if (pwdev->UsePlanarBuffer)
  223.     gdev_prn_set_procs_planar(pdev);
  224.  
  225.     /* Special writer open routine for async interpretation */
  226.     /* Starts render thread */
  227.     return gdev_prn_async_write_open((gx_device_printer *)pdev,
  228.                      max_raster, min_band_height,
  229.                      max_src_image_row);
  230. }
  231. private int
  232. bmpa_writer_open(gx_device *pdev  /* Driver instance to open */)
  233. {
  234.     return bmpa_open_writer(pdev, bmpa_reader_print_page_copies,
  235.                 bmpa_reader_buffer_page);
  236. }
  237. private int
  238. bmpa_cmyk_writer_open(gx_device *pdev  /* Driver instance to open */)
  239. {
  240.     return bmpa_open_writer(pdev, bmpa_cmyk_reader_print_copies,
  241.                 bmpa_cmyk_reader_buffer_page);
  242. }
  243.  
  244. /* -------------- Renderer instance procedures ----------*/
  245.  
  246. /* Forward declarations */
  247. private int
  248.     bmpa_reader_buffer_planes(P6(gx_device_printer *pdev, FILE *prn_stream,
  249.                  int num_copies, int first_plane,
  250.                  int last_plane, int raster));
  251.  
  252. /* Thread to do rendering, started by bmpa_reader_start_render_thread */
  253. private void 
  254. bmpa_reader_thread(void *params)
  255. {
  256.     gdev_prn_async_render_thread((gdev_prn_start_render_params *)params);
  257. }
  258.  
  259. private int    /* rets 0 ok, -ve error if couldn't start thread */
  260. bmpa_reader_start_render_thread(gdev_prn_start_render_params *params)
  261. {
  262.     return gp_create_thread(bmpa_reader_thread, params);
  263. }
  264.  
  265. private int
  266. bmpa_reader_open_render_device(gx_device_printer *ppdev)
  267. {
  268.     /*
  269.      * Do anything that needs to be done at open time here.
  270.      * Since this implementation doesn't do anything, we don't need to
  271.      * cast the device argument to the more specific type.
  272.      */
  273.     /*gx_device_async * const prdev = (gx_device_async *)ppdev;*/
  274.  
  275.     /* Cascade down to the default handler */
  276.     return gdev_prn_async_render_open(ppdev);
  277. }
  278.  
  279. /* Generic routine to send the page to the printer. */
  280. private int
  281. bmpa_reader_output_page(gx_device *pdev, int num_copies, int flush)
  282. {
  283.     /*
  284.      * HACK: open the printer page with the positionable attribute since
  285.      * we need to seek back & forth to support partial rendering.
  286.      */
  287.     if ( num_copies > 0 || !flush ) {
  288.     int code = gdev_prn_open_printer_positionable(pdev, 1, 1);
  289.  
  290.     if ( code < 0 )
  291.         return code;
  292.     }
  293.     return gdev_prn_output_page(pdev, num_copies, flush);
  294. }
  295.  
  296. private int
  297. bmpa_reader_print_planes(gx_device_printer *pdev, FILE *prn_stream,
  298.              int num_copies, int first_plane, int last_plane,
  299.              int raster)
  300. {
  301.     gx_device_async * const prdev = (gx_device_async *)pdev;
  302.     /* BMP scan lines are padded to 32 bits. */
  303.     uint bmp_raster = raster + (-raster & 3);
  304.     int code = 0;
  305.     int y;
  306.     byte *row = 0;
  307.     byte *raster_data;
  308.     int plane;
  309.  
  310.     /* If there's data in buffer, need to process w/overlays */
  311.     if (prdev->buffered_page_exists) {
  312.     code = bmpa_reader_buffer_planes(pdev, prn_stream, num_copies,
  313.                      first_plane, last_plane, raster);
  314.     goto done;
  315.     }
  316. #ifdef SINGLE_PAGE
  317.     /* BMP format is single page, so discard all but 1st printable page */
  318.     /* Since the OutputFile may have a %d, we use ftell to determine if */
  319.     /* this is a zero length file, which is legal to write        */
  320.     if (ftell(prn_stream) != 0)
  321.     return 0;
  322. #endif
  323.     row = gs_alloc_bytes(pdev->memory, bmp_raster, "bmp file buffer");
  324.     if (row == 0)        /* can't allocate row buffer */
  325.     return_error(gs_error_VMerror);
  326.  
  327.     for (plane = first_plane; plane <= last_plane; ++plane) {
  328.     gx_render_plane_t render_plane;
  329.  
  330.     /* Write header & seek to its end */
  331.     code =
  332.         (first_plane < 0 ? write_bmp_header(pdev, prn_stream) :
  333.          write_bmp_separated_header(pdev, prn_stream));
  334.     if (code < 0)
  335.         goto done;
  336.     /* Save the file offset where data begins */
  337.     if ((prdev->file_offset_to_data[plane - first_plane] =
  338.          ftell(prn_stream)) == -1L) {
  339.         code = gs_note_error(gs_error_ioerror);
  340.         goto done;
  341.     }
  342.  
  343.     /*
  344.      * Write out the bands top to bottom.  Finish the job even if
  345.      * num_copies == 0, to avoid invalid output file.
  346.      */
  347.     if (plane >= 0)
  348.         gx_render_plane_init(&render_plane, (gx_device *)pdev, plane);
  349.     for (y = prdev->height - 1; y >= 0; y--) {
  350.         uint actual_raster;
  351.  
  352.         code = gdev_prn_get_lines(pdev, y, 1, row, bmp_raster,
  353.                       &raster_data, &actual_raster,
  354.                       (plane < 0 ? NULL : &render_plane));
  355.         if (code < 0)
  356.         goto done;
  357.         if (fwrite((const char *)raster_data, actual_raster, 1, prn_stream) < 1) {
  358.         code = gs_error_ioerror;
  359.         goto done;
  360.         }
  361.     }
  362.     }
  363. done:
  364.     gs_free_object(pdev->memory, row, "bmp file buffer");
  365.     prdev->buffered_page_exists = 0;
  366.     return code;
  367. }
  368. private int
  369. bmpa_reader_print_page_copies(gx_device_printer *pdev, FILE *prn_stream,
  370.                   int num_copies)
  371. {
  372.     return bmpa_reader_print_planes(pdev, prn_stream, num_copies, -1, -1,
  373.                     gdev_prn_raster(pdev));
  374. }
  375. private int
  376. bmpa_cmyk_plane_raster(gx_device_printer *pdev)
  377. {
  378.     return bitmap_raster(pdev->width * (pdev->color_info.depth / 4));
  379. }
  380. private int
  381. bmpa_cmyk_reader_print_copies(gx_device_printer *pdev, FILE *prn_stream,
  382.                   int num_copies)
  383. {
  384.     return bmpa_reader_print_planes(pdev, prn_stream, num_copies, 0, 3,
  385.                     bmpa_cmyk_plane_raster(pdev));
  386. }
  387.  
  388. /* Buffer a (partial) rasterized page & optionally print result multiple times. */
  389. private int
  390. bmpa_reader_buffer_planes(gx_device_printer *pdev, FILE *file, int num_copies,
  391.               int first_plane, int last_plane, int raster)
  392. {
  393.     gx_device_async * const prdev = (gx_device_async *)pdev;
  394.     gx_device * const dev = (gx_device *)pdev;
  395.     int code = 0;
  396.  
  397.     /* If there's no data in buffer, no need to do any overlays */
  398.     if (!prdev->buffered_page_exists) {
  399.     code = bmpa_reader_print_planes(pdev, file, num_copies,
  400.                     first_plane, last_plane, raster);
  401.     goto done;
  402.     }
  403.  
  404.     /*
  405.      * Continue rendering on top of the existing file. This requires setting
  406.      * up a buffer of the existing bits in GS's format (except for optional
  407.      * extra padding bytes at the end of each scan line, provided the scan
  408.      * lines are still correctly memory-aligned) and then calling
  409.      * gdev_prn_render_lines.  If the device already provides a band buffer
  410.      * -- which currently is always the case -- we can use it if we want;
  411.      * but if a device stores partially rendered pages in memory in a
  412.      * compatible format (e.g., a printer with a hardware page buffer), it
  413.      * can render directly on top of the stored bits.
  414.      *
  415.      * If we can render exactly one band (or N bands) at a time, this is
  416.      * more efficient, since otherwise (a) band(s) will have to be rendered
  417.      * more than once.
  418.      */
  419.  
  420.     {
  421.     byte *raster_data;
  422.     gx_device_clist_reader *const crdev =
  423.         (gx_device_clist_reader *)pdev;
  424.     int raster = gx_device_raster(dev, 1);
  425.     int padding = -raster & 3; /* BMP scan lines are padded to 32 bits. */
  426.     int bmp_raster = raster + padding;
  427.     int plane;
  428.  
  429.     /*
  430.      * Get the address of the renderer's band buffer.  In the future,
  431.      * it will be possible to suppress the allocation of this buffer,
  432.      * and to use only buffers provided the driver itself (e.g., a
  433.      * hardware buffer).
  434.      */
  435.     if (!pdev->buffer_space) {
  436.         /* Not banding.  Can't happen. */
  437.         code = gs_note_error(gs_error_Fatal);
  438.         goto done;
  439.     }
  440.     raster_data = crdev->data;
  441.  
  442.     for (plane = first_plane; plane <= last_plane; ++plane) {
  443.         gx_render_plane_t render_plane;
  444.         gx_device *bdev;
  445.         int y, band_base_line;
  446.  
  447.         /* Seek to beginning of data portion of file */
  448.         if (fseek(file, prdev->file_offset_to_data[plane - first_plane],
  449.               SEEK_SET)) {
  450.         code = gs_note_error(gs_error_ioerror);
  451.         goto done;
  452.         }
  453.  
  454.         if (plane >= 0)
  455.         gx_render_plane_init(&render_plane, (gx_device *)pdev, plane);
  456.         else
  457.         render_plane.index = -1;
  458.  
  459.         /* Set up the buffer device. */
  460.         code = gdev_create_buf_device(crdev->buf_procs.create_buf_device,
  461.                       &bdev, crdev->target, &render_plane,
  462.                       dev->memory, true);
  463.         if (code < 0)
  464.         goto done;
  465.  
  466.         /*
  467.          * Iterate thru bands from top to bottom.  As noted above, we
  468.          * do this an entire band at a time for efficiency.
  469.          */
  470.         for (y = dev->height - 1; y >= 0; y = band_base_line - 1) {
  471.         int band_height =
  472.             dev_proc(dev, get_band)(dev, y, &band_base_line);
  473.         int line;
  474.         gs_int_rect band_rect;
  475.  
  476.         /* Set up the buffer device for this band. */
  477.         code = crdev->buf_procs.setup_buf_device
  478.             (bdev, raster_data, bmp_raster, NULL, 0, band_height,
  479.              band_height);
  480.         if (code < 0)
  481.             goto done;
  482.  
  483.         /* Fill in the buffer with a band from the BMP file. */
  484.         /* Need to do this backward since BMP is top to bottom. */
  485.         for (line = band_height - 1; line >= 0; --line)
  486.             if (fread(raster_data + line * bmp_raster,
  487.                   raster, 1, file) < 1 ||
  488.             fseek(file, padding, SEEK_CUR)
  489.             ) {
  490.             code = gs_note_error(gs_error_ioerror);
  491.             goto done;
  492.             }
  493.  
  494.         /* Continue rendering on top of the existing bits. */
  495.         band_rect.p.x = 0;
  496.         band_rect.p.y = band_base_line;
  497.         band_rect.q.x = pdev->width;
  498.         band_rect.q.y = band_base_line + band_height;
  499.         if ((code = clist_render_rectangle((gx_device_clist *)pdev,
  500.                            &band_rect, bdev,
  501.                            &render_plane, false)) < 0)
  502.             goto done;
  503.  
  504.         /* Rewind & write out the updated buffer. */
  505.         if (fseek(file, -bmp_raster * band_height, SEEK_CUR)) {
  506.             code = gs_note_error(gs_error_ioerror);
  507.             goto done;
  508.         }
  509.         for (line = band_height - 1; line >= 0; --line) {
  510.             if (fwrite(raster_data + line * bmp_raster,
  511.                    bmp_raster, 1, file) < 1 ||
  512.             fseek(file, padding, SEEK_CUR)
  513.             ) {
  514.             code = gs_note_error(gs_error_ioerror);
  515.             goto done;
  516.             }
  517.         }
  518.         }
  519.         crdev->buf_procs.destroy_buf_device(bdev);
  520.     }
  521.     }
  522.  
  523.  done:
  524.     prdev->buffered_page_exists = (code >= 0);
  525.     return code;
  526. }
  527. private int
  528. bmpa_reader_buffer_page(gx_device_printer *pdev, FILE *prn_stream,
  529.             int num_copies)
  530. {
  531.     return bmpa_reader_buffer_planes(pdev, prn_stream, num_copies, -1, -1,
  532.                      gdev_prn_raster(pdev));
  533. }
  534. private int
  535. bmpa_cmyk_reader_buffer_page(gx_device_printer *pdev, FILE *prn_stream,
  536.                  int num_copies)
  537. {
  538.     return bmpa_reader_buffer_planes(pdev, prn_stream, num_copies, 0, 3,
  539.                      bmpa_cmyk_plane_raster(pdev));
  540. }
  541.  
  542. /*------------ Procedures common to writer & renderer -------- */
  543.  
  544. /* Compute space parameters */
  545. private void
  546. bmpa_get_space_params(const gx_device_printer *pdev,
  547.  gdev_prn_space_params *space_params)
  548. {
  549.     /* Plug params into device before opening it
  550.      *
  551.      * You ask "How did you come up with these #'s?" You asked, so...
  552.      *
  553.      * To answer clearly, let me begin by recapitulating how command list
  554.      * (clist) device memory allocation works in the non-overlapped case:
  555.      * When the device is opened, a buffer is allocated. How big? For
  556.      * starters, it must be >= PRN_MIN_BUFFER_SPACE, and as we'll see, must
  557.      * be sufficient to satisfy the rest of the band params. If you don't
  558.      * specify a size for it in space_params.band.BandBufferSpace, the open
  559.      * routine will use a heuristic where it tries to use PRN_BUFFER_SPACE,
  560.      * then works its way down by factors of 2 if that much memory isn't
  561.      * available.
  562.      *
  563.      * The device proceeds to divide the buffer into several parts: one of
  564.      * them is used for the same thing during writing & rasterizing; the
  565.      * other parts are redivided and used differently writing and
  566.      * rasterizing. The limiting factor dictating memory requirements is the
  567.      * rasterizer's render buffer.  This buffer needs to be able to contain
  568.      * a pixmap that covers an entire band. Memory consumption is whatever
  569.      * is needed to hold N rows of data aligned on word boundaries, +
  570.      * sizeof(pointer) for each of N rows. Whatever is left over in the
  571.      * rasterized is allocated to a tile cache. You want to make sure that
  572.      * cache is at least 50KB.
  573.      *
  574.      * For example, take a 600 dpi b/w device at 8.5 x 11 inches.  For the
  575.      * whole device, that's 6600 rows @ 638 bytes = ~4.2 MB total.  If the
  576.      * device is divided into 100 bands, each band's rasterizer buffer is
  577.      * 62K. Add on a 50K tile cache, and you get a 112KB (+ add a little
  578.      * slop) total device buffer size.
  579.      *
  580.      * Now that we've covered the rasterizer, let's switch back to the
  581.      * writer. The writer must have a tile cache *exactly* the same size as
  582.      * the reader. This means that the space to divide up for the writer is
  583.      * equal is size to the rasterizer's band buffer.  This space is divided
  584.      * into 2 sections: per-band bookeeping info and a command buffer. The
  585.      * bookeeping info currently uses ~72 bytes for each band. The rest is
  586.      * the command buffer.
  587.      *
  588.      * To continue the same 112KB example, we have 62KB to slice up.
  589.      * We need 72 bytes * 100 bands = 7.2KB, leaving a 55K command buffer.
  590.      *
  591.      * A larger command buffer has some performance (see gxclmem.c comments)
  592.      * advantages in the general case, but is critical in one special case:
  593.      * high-level images. Whenever possible, images are transmitted across
  594.      * the band buffer in their original resolution and bits/pixel. The
  595.      * alternative fallback behavior can be very slow.  Here, the relevant
  596.      * restriction is that at least one entire source image row must fit
  597.      * into the command buffer. This means that, in our example, an RGB
  598.      * source image would have to be <= 18K pixels wide. If the image is
  599.      * sampled at the same resolution as the hardware (600 dpi), that means
  600.      * the row would be limited to a very reasonable 30 inches. However, if
  601.      * the source image is sampled at 2400 dpi, that limit is only 7.5
  602.      * inches. The situation gets worse as bands get smaller, but the
  603.      * implementor must decide on the tradeoff point.
  604.      *
  605.      * The moral of the story is that you should never make a band
  606.      * so small that its buffer limits the command buffer excessively.
  607.      * Again, Max image row bytes = band buffer size - # bands * 72. 
  608.      *
  609.      * In the overlapped case, everything is exactly as above, except that
  610.      * two identical devices, each with an identical buffer, are allocated:
  611.      * one for the writer, and one for the rasterizer. Because it's critical
  612.      * to allocate identical buffers, I *strongly* recommend setting these
  613.      * params in the writer's open routine:
  614.      * space_params.band.BandBufferSpace, .BandWidth and .BandHeight.  If
  615.      * you don't force these values to a known value, the memory allocation
  616.      * heuristic may not come to the same result for both copies of the
  617.      * device, since the first allocation will diminish the amount of free
  618.      * memory.
  619.      *
  620.      * There is room for an important optimization here: allocate the
  621.      * writer's space with enough memory for a generous command buffer, but
  622.      * allocate the reader with only enough memory for a band rasterization
  623.      * buffer and the tile cache.  To do this, observe that the space_params
  624.      * struct has two sizes: BufferSpace vs. BandBufferSpace.  To start,
  625.      * BandBufferSpace is always <= BufferSpace. On the reader side,
  626.      * BandBufferSpace is divided between the tile cache and the rendering
  627.      * buffer -- that's all the memory that's needed to rasterize. On the
  628.      * writer's side, BandBufferSpace is divided the same way: the tile
  629.      * cache (which must be identical to the reader's) is carved out, and
  630.      * the space that would have been used for a rasterizing buffer is used
  631.      * as a command buffer. However, you can further increase the cmd buf
  632.      * further by setting BufferSize (not BandBufferSize) to a higher number
  633.      * than BandBufferSize. In that case, the command buffer is increased by
  634.      * the difference (BufferSize - BandBufferSize). There is logic in the
  635.      * memory allocation for printers that will automatically use BufferSize
  636.      * for writers (or non-async printers), and BandBufferSize for readers.
  637.      *
  638.      * Note: per the comments in gxclmem.c, the banding logic will perform
  639.      * better with 1MB or better for the command list.
  640.      */
  641.     
  642.     /* This will give us a very "ungenerous" buffer. */
  643.     /* Here, my arbitrary rule for min image row is: twice the dest width */
  644.     /* in full CMYK. */
  645.     int render_space;
  646.     int writer_space;
  647.     const int tile_cache_space = 50 * 1024;
  648.     const int min_image_rows = 2;
  649.     int min_row_space =
  650.     min_image_rows * (  4 * ( pdev->width + sizeof(int) - 1 )  );
  651.     int min_band_height = max(1, pdev->height / 100);    /* make bands >= 1% of total */
  652.  
  653.     space_params->band.BandWidth = pdev->width;
  654.     space_params->band.BandHeight = min_band_height;
  655.  
  656.     render_space = gdev_mem_data_size( (const gx_device_memory *)pdev,
  657.                        space_params->band.BandWidth,
  658.                        space_params->band.BandHeight );
  659.     /* need to include minimal writer requirements to satisfy rasterizer init */
  660.     writer_space =     /* add 5K slop for good measure */
  661.     5000 + (72 + 8) * ( (pdev->height / space_params->band.BandHeight) + 1 );
  662.     space_params->band.BandBufferSpace =
  663.     max(render_space, writer_space) + tile_cache_space;
  664.     space_params->BufferSpace =
  665.     max(render_space, writer_space + min_row_space) + tile_cache_space;
  666.     /**************** HACK HACK HACK ****************/
  667.     /* Override this computation to force reader & writer to match */
  668.     space_params->BufferSpace = space_params->band.BandBufferSpace;
  669. }
  670.  
  671. /* Get device parameters. */
  672. private int
  673. bmpa_get_params(gx_device * pdev, gs_param_list * plist)
  674. {
  675.     gx_device_async * const bdev = (gx_device_async *)pdev;
  676.  
  677.     return gdev_prn_get_params_planar(pdev, plist, &bdev->UsePlanarBuffer);
  678. }
  679.  
  680. /* Put device parameters. */
  681. /* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/
  682. /* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/
  683. /* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/
  684. /* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/
  685. private int
  686. bmpa_put_params(gx_device *pdev, gs_param_list *plist)
  687. {
  688.     /*
  689.      * This driver does nothing interesting except cascade down to
  690.      * gdev_prn_put_params_planar, which is something it would have to do
  691.      * even if it did do something interesting here.
  692.      *
  693.      * Note that gdev_prn_put_params[_planar] does not close the device.
  694.      */
  695.     gx_device_async * const bdev = (gx_device_async *)pdev;
  696.  
  697.     return gdev_prn_put_params_planar(pdev, plist, &bdev->UsePlanarBuffer);
  698. }
  699.  
  700. /* Get hardware-detected parameters. */
  701. /* This proc defines a only one param: a useless value for testing */
  702. private int
  703. bmpa_get_hardware_params(gx_device *dev, gs_param_list *plist)
  704. {
  705.     static const char *const test_value = "Test value";
  706.     static const char *const test_name = "TestValue";
  707.     int code = 0;
  708.  
  709.     if ( param_requested(plist, test_name) ) {
  710.     gs_param_string param_str;
  711.  
  712.     param_string_from_string(param_str, test_value); /* value must be persistent to use this macro */
  713.     code = param_write_string(plist, test_name, ¶m_str);
  714.     }
  715.     return code;
  716. }
  717.